package org.mbds.wolf.seql;

import java.util.Arrays;

import org.mbds.wolf.exception.ByteArrayToHexaStringException;
import org.mbds.wolf.seql.ISO7816;
import org.mbds.wolf.tools.BytesTool;

/**
 * Usual / standard APDUS
 * @author amlesas
 */
public class APDUModel {

	// Standard CLA bytes in the APDU command
	public final static byte CLA_ISO7816 					= (byte) 0x00;
	public final static byte CLA_ISO7816_SECURED 			= (byte) 0x04;
	public final static byte CLA_GP_PROPRIETARY 			= (byte) 0x80;
	public final static byte CLA_GP_PRPOPRIETARY_SECURED 	= (byte) 0x84;
	public final static byte CLA_ISO7816_4_INS			 	= (byte) 0xB0; //to CF
	public final static byte CLA_RESERVED				 	= (byte) 0xFF; //Reader?

	// Standard INS bytes in the APDU command 
	public final static byte INS_DEACTIVATE				= (byte) 0x04;//ISO/IEC 7816-9
	public final static byte INS_ERASE_BINARY			= (byte) 0x0E;//ISO/IEC 7816-4
	public final static byte INS_PERFORM_SQL_INS		= (byte) 0x10;//SCQL ISO/IEC 7816-7
	public final static byte INS_PERFORM_SQL_TRANSAC	= (byte) 0x12;//SCQL ISO/IEC 7816-7
	public final static byte INS_PERFORM_USER_OP		= (byte) 0x14;//SCQL ISO/IEC 7816-7
	public final static byte INS_UNLOCK_APP				= (byte) 0x18;//EMV
	public final static byte INS_LOCK_APP				= (byte) 0x1E;//EMV
	public final static byte INS_VERIFY_PIN				= (byte) 0x20;//ISO/IEC 7816-4, EMV
	public final static byte INS_MANAGE_SECURITY		= (byte) 0x22;//ISO/IEC 7816-8
	public final static byte INS_CHANGE_PIN				= (byte) 0x24;//ISO/IEC 7816-8
	public final static byte INS_DISABLE_PIN			= (byte) 0x26;//ISO/IEC 7816-8
	public final static byte INS_ENABLE_PIN				= (byte) 0x28;//ISO/IEC 7816-8
	public final static byte INS_PERFORM_SECURITY_OP	= (byte) 0x2A;//ISO/IEC 7816-8
	public final static byte INS_RESET_RETRY_COUNTER	= (byte) 0x2C;//ISO/IEC 7816-8
	public final static byte INS_DECREASE_COUNTER		= (byte) 0x30;//EN 726-3
	public final static byte INS_INCREASE_COUNTER		= (byte) 0x32;//EN 726-3
	public final static byte INS_DECREASE_WITH_CHKSUM	= (byte) 0x34;//EN 726-3
	public final static byte INS_INCREASE_WITH_CHKSUM	= (byte) 0x36;//EN 726-3
	public final static byte INS_ACTIVATE				= (byte) 0x44;//ISO/IEC 7816-9
	public final static byte INS_GENERATE_KEY_PAIR		= (byte) 0x46;//ISO/IEC 7816-8
	public final static byte INS_INIT_PURSE				= (byte) 0x50;//EN 1546-3 {prepaid memory card}
	public final static byte INS_LOAD_PURSE				= (byte) 0x52;//EN 1546-3
	public final static byte INS_PAY_FROM_PURSE			= (byte) 0x54;//EN 1546-3
	public final static byte INS_CONVERT_CURRENCY		= (byte) 0x56;//EN 1546-3
	public final static byte INS_CHANGE_PURSE_PARAM		= (byte) 0x58;//EN 1546-3
	public final static byte INS_GET_LAST_PURSE_SIGN	= (byte) 0x5A;//EN 1546-3
	public final static byte INS_INSTALL_INTERNAL		= (byte) 0x6E;//EN 1546-3
	public final static byte INS_MANAGE_CHANNEL			= (byte) 0x70;//ISO/IEC 7816-4 
	public final static byte INS_INIT_PSAM				= (byte) 0x70;//EN 1546-3 {Purse security application module} 
	public final static byte INS_CREDIT_PSAM			= (byte) 0x72;//EN 1546-3 
	public final static byte INS_PSAM_COMPLETE			= (byte) 0x74;//EN 1546-3 
	public final static byte INS_INIT_ONLINE_COLLECT	= (byte) 0x76;//EN 1546-3 
	public final static byte INS_ONLINE_COLLECT			= (byte) 0x78;//EN 1546-3 
	public final static byte INS_END_ONLINE_COLLECT		= (byte) 0x7A;//EN 1546-3 
	public final static byte INS_INIT_OFFLINE_COLLECT	= (byte) 0x7C;//EN 1546-3 
	public final static byte INS_END_OFFLINE_COLLECT	= (byte) 0x7E;//EN 1546-3 
	public final static byte INS_INIT_PSAM_UPDATE		= (byte) 0x80;//EN 1546-3 
	public final static byte INS_MUTUAL_AUTHENTICATE	= (byte) 0x82;//ISO/IEC 7816-4
	public final static byte INS_PSAM_UPDATE_OFFLINE	= (byte) 0x84;//ISO/IEC 7816-4
	public final static byte INS_GET_CHALLENGE			= (byte) 0x84;//ISO/IEC 7816-4
	public final static byte INS_GET_LAST_PSAM_SIGN		= (byte) 0x86;//EN 1546-3 
	public final static byte INS_GENERAL_AUTHENTICATE	= (byte) 0x86;//GSM?
	public final static byte INS_INTERNAL_ANTHENTICATE	= (byte) 0x88;//ISO/IEC 7816-4
	public final static byte INS_COMPUTE_DIGITAL_SIGN	= (byte) 0x9E;//ISO/IEC 7816-9
	public final static byte INS_SEARCH_BINARY			= (byte) 0xA0;//ISO/IEC 7816-9
	public final static byte INS_SEARCH_RECORD			= (byte) 0xA2;//ISO/IEC 7816-9
	public final static byte INS_SELECT_FILE 			= (byte) 0xA4; //instruction for the ISO/IEC 7816-4 SELECT FILE command
	public final static byte INS_SELECT_DATA			= (byte) 0xA5;//OMAPI
	public final static byte INS_RESET_APP				= (byte) 0xAC;//EN 726-3
	public final static byte INS_EXEC_FILE				= (byte) 0xAE;//EN 726-3
	public final static byte INS_GENERATE_AUTH_SIGN		= (byte) 0xAE;//EMV
	public final static byte INS_READ_BINARY			= (byte) 0xB0;//ISO/IEC 7816-4
	public final static byte INS_READ_VALUE				= (byte) 0xB1;//MIFARE?
	public final static byte INS_READ_RECORD			= (byte) 0xB2;//ISO/IEC 7816-4
	public final static byte INS_GET_DATA_ID			= (byte) 0xB2;//OMAPI
	public final static byte INS_READ_BINARY_WITH_CHK	= (byte) 0xB4;//ISO/IEC 7816-4
	public final static byte INS_READ_RECORD_WITH_CHK	= (byte) 0xB6;//EN 726-3
	public final static byte INS_VERIFY_CERTIFICATE		= (byte) 0xBE;
	public final static byte INS_GET_RESPONSE			= (byte) 0xC0;//T0 protocol
	public final static byte INS_ENVELOPPE				= (byte) 0xC2;//ISO/IEC 7816-4 
	public final static byte INS_GET_DATA				= (byte) 0xCA;//ISO/IEC 7816-4
	public final static byte INS_WRITE_BINARY			= (byte) 0xD0;//ISO/IEC 7816-4
	public final static byte INS_WRITE_RECORD			= (byte) 0xD2;//ISO/IEC 7816-4
	public final static byte INS_EXTEND					= (byte) 0xD4;//EN 726-3
	public final static byte INS_UPDATE_BINARY			= (byte) 0xD6;//ISO/IEC 7816-4
	public final static byte INS_STORE_VALUE			= (byte) 0xD7;//MIFARE
	public final static byte INS_PUT_KEY 				= (byte) 0xD8;//ISO/IEC 7816-4
	public final static byte INS_PUT_DATA 				= (byte) 0xDA;//ISO/IEC 7816-4
	public final static byte INS_UPDATE_RECORD			= (byte) 0xDC;//ISO/IEC 7816-4
	public final static byte INS_CREATE_FILE			= (byte) 0xE0;//ISO/IEC 7816-9	
	public final static byte INS_APPEND_RECORD			= (byte) 0xE2;//ISO/IEC 7816-4	
	public final static byte INS_DELETE_DATA			= (byte) 0xE4;//ISO/IEC 7816-9, OMAPI	
	public final static byte INS_DELETE_ALL_DATA		= (byte) 0xE5;//ISO/IEC 7816-9, OMAPI	
	public final static byte INS_TERMINATE_DF			= (byte) 0xE6;//ISO/IEC 7816-9	
	public final static byte INS_TERMINATE_EF			= (byte) 0xE8;//ISO/IEC 7816-9	
	public final static byte INS_SET_STATUS 			= (byte) 0xF0;
	public final static byte INS_GET_STATUS 			= (byte) 0xF2;
	public final static byte INS_MIFARE_CLASSIC_READ	= (byte) 0xF3;
	public final static byte INS_MIFARE_CLASSIC_WRITE	= (byte) 0xF4;
	public final static byte INS_MIFARE_CLASSIC_VALUE	= (byte) 0xF5;
	public final static byte INS_TERMINATE_CARD			= (byte) 0xFE;//Irreversible
	
	// Header for select command 
	public final static byte[] CMD_SELECT_AID 				= {CLA_ISO7816, INS_SELECT_FILE, INS_DEACTIVATE};
	
	// Status words 
	public final static byte[] SW_SUCCESS_MORE_DATA  	= {(byte)0x61, (byte)0x00};      	// more data available
	public final static byte[] SW_SUCCESS_T0		  	= {(byte)0x61, (byte)0x05};      	// SeqlCommand done
	//WARNINGS
	public final static byte[] SW_RAM_NOT_CHANGED      	= {(byte)0x62, (byte)0x00};      	// NV-Ram not changed       
	public final static byte[] SW_RAM_NOT_CHANGED1      = {(byte)0x62, (byte)0x01};      	// NV-Ram not changed 1     
	public final static byte[] SW_CORRUPTED_DATA       	= {(byte)0x62, (byte)0x81};      	// Part of data corrupted   
	public final static byte[] SW_EOF                	= {(byte)0x62, (byte)0x82};      	// End of file reached      
	public final static byte[] SW_INVALID_FILE         	= {(byte)0x62, (byte)0x83};      	// Invalidated file         
	public final static byte[] SW_INVALID_FORMAT       	= {(byte)0x62, (byte)0x84};      	// Invalid file control     

	public final static byte[] SW_VERIFICATION_FAILED 	= {(byte)0x63, (byte)0x00};			// Verification failed
	public final static byte[] SW_PIN_REQUIRED 			= {(byte)0x63, (byte)0x01};			// Verification failed, PIN required
	public final static byte[] SW_VERIF_FAILED_COUNTER	= {(byte)0x63, (byte)0xC0};      	// Verification failed with counter     
	public final static byte[] SW_VERIF_FAILED_0_LEFT   = {(byte)0x63, (byte)0xC0};      	// Verification failed, no try left 
	public final static byte[] SW_VERIF_FAILED_1_LEFT   = {(byte)0x63, (byte)0xC1};      	// Verification failed, 1 try left  
	public final static byte[] SW_VERIF_FAILED_2_LEFT   = {(byte)0x63, (byte)0xC2};      	// Verification failed, 2 tries left
	public final static byte[] SW_VERIF_FAILED_3_LEFT   = {(byte)0x63, (byte)0xC3};      	// Verification failed, 3 tries left
	//EXECUTION ERRORS
	public final static byte[] SW_EXECUTION_ERROR       = {(byte)0x64, (byte)0x00};      	 
	public final static byte[] SW_TIMEOUT            	= {(byte)0x64, (byte)0x01};      	// SeqlCommand timeout       

	public final static byte[] SW_MEMORY_ERROR        	= {(byte)0x65, (byte)0x81};      	// Memory failure   
	//CHECKING ERRORS
	public final static byte[] SW_WRONG_LENGTH        	= {(byte)0x67, (byte)0x00};      	// Wrong length      

	public final static byte[] SW_CLA_NOT_SUPPORTED    	= {(byte)0x68, (byte)0x00};      	// CLA byte not supported 
	public final static byte[] SW_LC_NOT_SUPPORTED     	= {(byte)0x68, (byte)0x81};      	// Logical channel not supported 
	public final static byte[] SW_SM_NOT_SUPPORTED     	= {(byte)0x68, (byte)0x82};      	// Secure Messaging not supported 
	public final static byte[] SW_LAST_CMD_EXPECTED    	= {(byte)0x68, (byte)0x83};      	// Last command of the chain expected 
	public final static byte[] SW_CHAIN_NOT_SUPPORTED  	= {(byte)0x68, (byte)0x84};      	// SeqlCommand chaining not supported 

	public final static byte[] SW_COM_NOT_ALLOWED      	= {(byte)0x69, (byte)0x00};      	// SeqlCommand not allowed 
	public final static byte[] SW_COM_INCOMPATIBLE    	= {(byte)0x69, (byte)0x81};      	// SeqlCommand incompatible with file structure 
	public final static byte[] SW_SECUR_NOT_SATISFIED 	= {(byte)0x69, (byte)0x82};      	// Security condition not satisfied 
	public final static byte[] SW_AUTH_METHOD_LOCKED   	= {(byte)0x69, (byte)0x83};      	// Authentication method locked 
	public final static byte[] SW_REF_DATA_NOT_USABLE 	= {(byte)0x69, (byte)0x84};      	// Reference data not usable 
	public final static byte[] SW_COND_OF_USE_NOT_SAT   = {(byte)0x69, (byte)0x85};      	// Condition of use not satisfied 
	public final static byte[] SW_COM_NOT_ALLOW_NOEF   	= {(byte)0x69, (byte)0x86};      	// SeqlCommand not allowed (no current EF) 
	public final static byte[] SW_SEC_MSG_OBJ_MISSING   = {(byte)0x69, (byte)0x87};      	// Expected secure messaging object missing 
	public final static byte[] SW_INCORRECT_SM_DATA_OBJ	= {(byte)0x69, (byte)0x88};      	// Incorrect secure messaging data object 

	public final static byte[] SW_INVALID_PARAM        	= {(byte)0x6A, (byte)0x00};      	// Wrong parameter P1-P2 
	public final static byte[] SW_INVALID_DATA          = {(byte)0x6A, (byte)0x80};      	// Incorrect parameter in the command data field
	public final static byte[] SW_INS_NOT_SUPPORTED   	= {(byte)0x6A, (byte)0x81};      	// INS not supported 
	public final static byte[] SW_FILE_NOT_FOUND       	= {(byte)0x6A, (byte)0x82};      	// File not found    
	public final static byte[] SW_RECORD_NOT_FOUND     	= {(byte)0x6A, (byte)0x83};      	// Record not found    
	public final static byte[] SW_OUT_OF_MEMORY        	= {(byte)0x6A, (byte)0x84};      	// Not enough memory space in the file   
	public final static byte[] SW_INVALID_LC_WITH_TLV   = {(byte)0x6A, (byte)0x85};      	// Nc inconsistent with TLV structure 
	public final static byte[] SW_INVALID_ACCESS       	= {(byte)0x6A, (byte)0x85};      	// Access cond. n/f  
	public final static byte[] SW_INCORRECT_P1P2        = {(byte)0x6A, (byte)0x86};      	// Incorrect P1-P2   
	public final static byte[] SW_INVALID_LC            = {(byte)0x6A, (byte)0x87};      	// Lc inconsistent with P1-P2 
	public final static byte[] SW_REF_DATA_NOT_FOUND    = {(byte)0x6A, (byte)0x88};      	// Reference data not found
	public final static byte[] SW_FILE_EXISTS         	= {(byte)0x6A, (byte)0x89};      	// File already exists 
	public final static byte[] SW_DF_NAME_EXISTS       	= {(byte)0x6A, (byte)0x8A};      	// DF name already exists 

	public final static byte[] SW_INVALID_P1P2          = {(byte)0x6B, (byte)0x00};      	// Wrong parameter P1-P2 
	public final static byte[] SW_INVALID_LE            = {(byte)0x6C, (byte)0x00};      	// Invalid Le        
	public final static byte[] SW_INVALID_INS           = {(byte)0x6D, (byte)0x00};      	// Wrong instruction 
	public final static byte[] SW_INVALID_CLA           = {(byte)0x6E, (byte)0x00};      	// Class not supported 
	public final static byte[] SW_GENERAL_ERROR       	= {(byte)0x6F, (byte)0x00};      	// No precise diagnosis 
	//OTHER
	public final static byte[] SW_SUCCESS 				= {(byte)0x90, (byte)0x00};
	public final static byte[] SW_NO_MORE_STORAGE      	= {(byte)0x92, (byte)0x10};      	// No more storage available         
	public final static byte[] SW_ACCESS_NOT_SATISFIED  = {(byte)0x98, (byte)0x04};      	// Access conditions not satisfied   

	public final byte [] SELECT_APDU_VISA = {(byte)0x00,(byte)0xA4,(byte)0x04,(byte)0x00, (byte)0x07, 
			(byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x10, (byte)0x10};
	public final byte [] SELECT_APDU_MASTERCARD = {(byte)0x00,(byte)0xA4,(byte)0x04,(byte)0x00, (byte)0x07, 
			(byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x41, (byte)0x01, (byte)0x00};
	public final byte [] GET_PROCESSING_OPT = {(byte)(byte)0x80,(byte)0xA8,(byte)0x00,(byte)0x00,
			(byte)0x02,(byte)0x83,(byte)0x00};
	public final byte [] READ_RECORD_ZERO = {(byte)0x00,(byte)0xB2, (byte)0x01, (byte)0x0C, (byte)0x00};


	/**
	 * Check if the apdu command received is the select AppletModel command
	 * @param apduCommand
	 * @return
	 */
	public static boolean isCommandSelect(byte[] apduCommand) {
		if (apduCommand.length < 2) return false;
		return apduCommand[ISO7816.OFFSET_CLA] == CMD_SELECT_AID[ISO7816.OFFSET_CLA] && 
				apduCommand[ISO7816.OFFSET_INS] == CMD_SELECT_AID[ISO7816.OFFSET_INS];
	}
	/**
	 * 
	 * @return
	 */
	public static boolean isResponseSucceded(byte[] apduResponse) {
		if (apduResponse.length < 2) return false;
		byte[] success = Arrays.copyOfRange(apduResponse,apduResponse.length-2, apduResponse.length);
		return success[0] == SW_SUCCESS[0] && success[1] == SW_SUCCESS[1];
	}
	
	/**
	 * isPinRequired
	 * @param resp
	 * @return boolean isPinRequired
	 */
	public static boolean isPinRequired(byte[] apduResponse) {
		if (apduResponse.length < 2) return false;
		byte[] pinRequired = Arrays.copyOfRange(apduResponse,apduResponse.length-2, apduResponse.length);
		return pinRequired[0] == SW_PIN_REQUIRED[0] && pinRequired[1] == SW_PIN_REQUIRED[1];
	}

	/**
	 * getResponseStatusWord
	 * @param apdu
	 * @return array 2 bytes status words
	 */
	public static byte[] getResponseStatusWord(byte[] apdu) {
		return new byte[] { apdu[apdu.length - 2], apdu[apdu.length - 1] };
	}

	/**
	 * getResponseBody
	 * @param apdu
	 * @return response String
	 */
	public static String getResponseBody(byte[] apdu) {
		if (apdu.length<=2) return "";
		byte[] body = new byte[apdu.length-2];
		System.arraycopy(apdu, 0, body, 0, body.length);
		try {
			return BytesTool.byteArrayToHexString(body);
		} catch (ByteArrayToHexaStringException e) {
			e.printStackTrace();
			return "";
		}
	}
	
	/**
	 * getSelectAidApdu
	 * @param aid
	 * @return byte[] APDU command SELECT AID ISO 7816-4
	 */
	public static byte[] getSelectAidApdu(byte[] aid) {
		byte[] apdu = new byte[5+aid.length];
		System.arraycopy(CMD_SELECT_AID, 0, apdu, 0, CMD_SELECT_AID.length);
		System.arraycopy(aid, 0, apdu, 5, aid.length);
		byte[] size = BytesTool.integerToByteArray(aid.length, 1);
		System.arraycopy(size, 0, apdu, 4, 1);
		return apdu;
	}

}
